package com.ksyun.campus.metaserver.services;

import cn.hutool.core.net.NetUtil;
import com.ksyun.campus.common.constant.ZkConstant;
import com.ksyun.campus.common.domain.MetaServer;
import com.ksyun.campus.common.domain.ModeType;
import com.ksyun.campus.common.util.ByteConvertUtil;
import com.ksyun.campus.metaserver.bootstrap.MetaServerInstance;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.zookeeper.CreateMode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.List;

@Component
@Slf4j
public class RegistService implements ApplicationRunner {
    private String ipAddress = NetUtil.getLocalhostStr();

    @Autowired
    private CuratorFramework curatorFramework;

    @Value("${server.port}")
    private int port;

    @Value("${spring.application.name}")
    private String serviceName;

    private String nodePath;

    public void registToCenter() {
        // todo 将本实例信息注册至zk中心，包含信息 ip、port
        this.nodePath = String.format("%s/%s/%s:%s", ZkConstant.ROOT_PATH, serviceName, ipAddress, port);
        try {
            if (curatorFramework.checkExists().forPath( this.nodePath) == null) {
                MetaServer metaServerInstance = MetaServerInstance.getMateServer();
                curatorFramework.create()
                        .creatingParentsIfNeeded()
                        .withMode(CreateMode.EPHEMERAL)
                        .forPath( this.nodePath, ByteConvertUtil.toByte(metaServerInstance));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 初始化元数据路径
     */
    private void initMetaPath() {
        try {
            if (curatorFramework.checkExists().forPath(ZkConstant.META_DATA_PATH) == null) {
                curatorFramework.create()
                        .creatingParentsIfNeeded().forPath(ZkConstant.META_DATA_PATH);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 检查在zk中 是否存在了主节点
     * @return
     */
    private boolean checkHasMaster(){
        String path = String.format("%s/%s", ZkConstant.ROOT_PATH, serviceName);
        try {
            List<String> list = curatorFramework.getChildren().forPath(path);
            if(list.size() != 0){
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    /**
     * 服务注销
     */
    @PreDestroy
    public void unregister(){
        String path = String.format("%s/%s/%s:%s", ZkConstant.ROOT_PATH, serviceName, ipAddress, port);
        log.info("服务注销~");
        try {
            curatorFramework.delete().forPath(path);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run(ApplicationArguments args) {
        log.info("元数据服务注册~");
        MetaServerInstance.init(ipAddress, port);
        //检查是否存在主节点，第一个注册的作为主节点，其他为从节点
        if(checkHasMaster()){
            MetaServerInstance.getMateServer().setMode(ModeType.Follower);
        }else{
            MetaServerInstance.getMateServer().setMode(ModeType.Master);
        }
        registToCenter();
        initMetaPath();
    }


    /**
     * 检查集群状态 ，随时准备成为 主节点
     */
    @Scheduled(initialDelay = 20000, fixedDelay = 3000)
    public void checkCluster(){
        if(MetaServerInstance.getMateServer().getMode().equals(ModeType.Master)){
            //已经是主节点了，不对zk做任何检查
            return;
        }
        String path = String.format("%s/%s", ZkConstant.ROOT_PATH, serviceName);
        try {
            List<String> list = curatorFramework.getChildren().forPath(path);
            //只有自己一个节点， 那么就将自己更新为主节点
            if(list.size() == 1 && list.get(0).equals(String.format("%s:%s", ipAddress, port))){
                MetaServerInstance.getMateServer().setMode(ModeType.Master);
                curatorFramework.setData().forPath(this.nodePath, ByteConvertUtil.toByte(MetaServerInstance.getMateServer()));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
